package common

import (
	"bytes"
	"crypto/md5"
	crypto_rand "crypto/rand"
	"crypto/sha1"
	"encoding/base64"
	"encoding/binary"
	"encoding/hex"
	"encoding/json"
	"errors"
	"fmt"
	"github.com/astaxie/beego/logs"
	uuid "github.com/satori/go.uuid"
	"io"
	"math"
	"math/big"
	"math/rand"
	"mime/multipart"
	"reflect"
	"regexp"
	"runtime"
	"strconv"
	"strings"
	"time"
	"unicode"
	"unicode/utf8"
	"unsafe"
)

// GetLinkHTML 获取超链接标签
func GetLinkHTML(text string, url string) string {
	return fmt.Sprintf("<a href=\"%s\">%s</a>", url, text)
}

func MakeUUID() string {
	return uuid.NewV4().String()
}

// CheckMobile 手机号验证规则
func CheckMobile(num string) bool {
	phoneCheckRule := `^1[3-9]\d{9}$`
	matched, err := regexp.MatchString(phoneCheckRule, num)
	if err != nil || !matched {
		return false
	}
	return true
}

// Bool2Int bool 转 int true=1 false=0
func Bool2Int(val bool) int {
	if val {
		return 1
	}
	return 0
}

// Int2Bool int 转 bool true=1 false=0
func Int2Bool(val int) bool {
	return val > 0
}

// CheckTimeRange 确认时间范围
func CheckTimeRange(startTime, endTime int64) (int64, int64, error) {
	currentTime := time.Now()
	start := time.Unix(startTime, 0)
	end := time.Unix(endTime+86400, 0)

	if startTime == 0 && endTime == 0 {
		start = currentTime.AddDate(0, 0, -7)
		end = currentTime
	}

	if end.Before(start) {
		err := errors.New("开始时间大于结束时间")
		logs.Error(err)
		return 0, 0, err
	}

	return ZeroTime(start).Unix(), ZeroTime(end).Unix(), nil
}

// TelVague 电话号码 中间四位*处理   isRole 是否需要判断角色
func TelVague(tel string, role int, isRole bool) string {
	isVague := false
	if tel != "" {
		if isRole {
			if role != 1 {
				isVague = true
			}
		} else {
			isVague = true
		}
		if isVague {
			if len(tel) <= 10 { //固定电话
				return tel
			}
			return tel[:3] + "****" + tel[7:]
		}
	}
	return tel
}

// CheckType 通用类型比较
func CheckType(str string, ct string) bool {
	if str == "" || ct == "" {
		return false
	}
	var pattern string
	switch strings.ToLower(ct) {
	case "email":
		pattern = `^[0-9a-z][_.0-9a-z-]{0,31}@([0-9a-z][0-9a-z-]{0,30}[0-9a-z]\.){1,4}[a-z]{2,4}$`
	case "phone":
		pattern = `^1\d{10}$`
	case "url":
		pattern = `^(http|https):\/\/.*$`
	case "number":
		pattern = `^\d+$`
	default:
		return false
	}
	reg := regexp.MustCompile(pattern)
	return reg.MatchString(str)
}

// GetKeys 获取key
func GetKeys(m map[string]int) []string {
	// 数组默认长度为map长度,后面append时,不需要重新申请内存和拷贝,效率很高
	keys := make([]string, 0, len(m))
	for k := range m {
		keys = append(keys, k)
	}
	return keys
}

// StructToMap 结构体转map
func StructToMap(obj interface{}) map[string]interface{} {
	k := reflect.TypeOf(obj)
	v := reflect.ValueOf(obj)

	var data = make(map[string]interface{})
	for i := 0; i < k.NumField(); i++ {
		data[strings.ToLower(k.Field(i).Name)] = v.Field(i).Interface()
	}
	return data
}

// MapToStruct map转结构体
func MapToStruct(objMap interface{}, obj interface{}) {
	jsonByte, _ := json.Marshal(objMap)
	err := json.Unmarshal(jsonByte, &obj)
	if err != nil {
		logs.Error(err)
	}
}

// StructToMapWithTag 结构体转map，根据结构体tag(json)来设置key
func StructToMapWithTag(obj interface{}, noNil ...bool) map[string]interface{} {
	k := reflect.TypeOf(obj)
	v := reflect.ValueOf(obj)

	var data = make(map[string]interface{})
	for i := 0; i < k.NumField(); i++ {

		if len(noNil) > 0 && noNil[0] == true {
			if v.Field(i).IsZero() {
				continue
			}
		}
		data[k.Field(i).Tag.Get("json")] = v.Field(i).Interface()
	}
	return data
}

// FriendlyFloat64 保留两位小数
func FriendlyFloat64(fv float64) float64 {
	val, err := strconv.ParseFloat(fmt.Sprintf("%.2f", fv), 64)
	if err != nil {
		return fv
	}
	return val
}

// ThreeFriendlyFloat64 保留三位小数
func ThreeFriendlyFloat64(fv float64) float64 {
	val, err := strconv.ParseFloat(fmt.Sprintf("%.3f", fv), 64)
	if err != nil {
		return fv
	}
	return val
}

func FloatToInt64(fv float64) int64 {
	fti, _ := strconv.Atoi(fmt.Sprintf("%1.0f", fv))
	return int64(fti)
}

// IntToBytes int转bytes
func IntToBytes(n int) []byte {
	x := int32(n)
	bytesBuffer := bytes.NewBuffer([]byte{})
	_ = binary.Write(bytesBuffer, binary.BigEndian, x)
	return bytesBuffer.Bytes()
}

// Uint32ToBytes int转bytes
func Uint32ToBytes(n uint32) []byte {
	bytesBuffer := bytes.NewBuffer([]byte{})
	_ = binary.Write(bytesBuffer, binary.BigEndian, n)
	return bytesBuffer.Bytes()
}

// Int32ToBytes int转bytes
func Int32ToBytes(n int32) []byte {
	bytesBuffer := bytes.NewBuffer([]byte{})
	_ = binary.Write(bytesBuffer, binary.BigEndian, n)
	return bytesBuffer.Bytes()
}

// Int64ToBytes int转bytes
func Int64ToBytes(n int64) []byte {
	bytesBuffer := bytes.NewBuffer([]byte{})
	_ = binary.Write(bytesBuffer, binary.BigEndian, n)
	return bytesBuffer.Bytes()
}

// IntToInt64 int转int64
func IntToInt64(a []int) []int64 {
	b := make([]int64, 0)
	for _, v := range a {
		b = append(b, int64(v))
	}
	return b
}

// IntToInt64 int转int64
func UInt32ToInt(a []uint32) []int {
	b := make([]int, 0)
	for _, v := range a {
		b = append(b, int(v))
	}
	return b
}

// BytesToUint32 ...
func BytesToUint32(b []byte) uint32 {
	bytesBuffer := bytes.NewBuffer(b)
	var x uint32
	binary.Read(bytesBuffer, binary.BigEndian, &x)
	return x
}

// BytesToInt32 ...
func BytesToInt32(b []byte) int32 {
	bytesBuffer := bytes.NewBuffer(b)
	var x int32
	binary.Read(bytesBuffer, binary.BigEndian, &x)
	return x
}

// BytesToInt64 byte转int64
func BytesToInt64(b []byte) int64 {
	bytesBuffer := bytes.NewBuffer(b)
	var x int64
	binary.Read(bytesBuffer, binary.BigEndian, &x)
	return x
}

// InArrayInt 是否在int数组中
func InArrayInt(need int, needArr []int) bool {
	for _, v := range needArr {
		if need == v {
			return true
		}
	}
	return false
}

// InArrayInt64 是否在int数组中
func InArrayInt64(need int64, needArr []int64) bool {
	for _, v := range needArr {
		if need == v {
			return true
		}
	}
	return false
}

// InArrayString 是否在string数组中
func InArrayString(need string, needArr []string) bool {
	for _, v := range needArr {
		if need == v {
			return true
		}
	}
	return false
}

// AddSlashes 函数返回在预定义字符之前添加反斜杠的字符串。
func AddSlashes(str string) string {
	tmpRune := make([]rune, 0)
	//strRune := []rune(str)
	for _, ch := range str {
		switch ch {
		case []rune{'\\'}[0], []rune{'"'}[0], []rune{'\''}[0]:
			tmpRune = append(tmpRune, []rune{'\\'}[0])
			tmpRune = append(tmpRune, ch)
		default:
			tmpRune = append(tmpRune, ch)
		}
	}
	return string(tmpRune)
}

// StripSlashes 函数删除由 AddSlashes() 函数添加的反斜杠。
func StripSlashes(str string) string {
	dstRune := make([]rune, 0)
	strRune := []rune(str)
	strLength := len(strRune)
	for i := 0; i < strLength; i++ {
		if strRune[i] == []rune{'\\'}[0] {
			i++
		}
		dstRune = append(dstRune, strRune[i])
	}
	return string(dstRune)
}

// ExplainNumberInterval 解析区间范围的字符串为m-n，
// 0-10000 firstNum=0, secondNum=10000
// 5000- firstNum=5000 secondNum=0
// secondNum < firstNum 的时候表示不设上限
func ExplainNumberInterval(str string, delta int) (firstNum float64, secondNum float64) {
	firstNum = 0
	secondNum = 0
	num := strings.Split(str, "-")
	firstNum = ParseStringFloat64(num[0])

	if len(num) > 1 && num[1] != "" {
		secondNum = ParseStringFloat64(num[1])
	}

	if delta != 1 {
		firstNum *= float64(delta)
		secondNum *= float64(delta)
	}

	return
}

// ParseStringFloat64 ...
func ParseStringFloat64(s string) float64 {
	f, _ := strconv.ParseFloat(s, 64)
	return f
}

// ParseFloat64ToString 解析float64成string
func ParseFloat64ToString(f float64, prec int) string {
	return strconv.FormatFloat(f, 'f', prec, 64)
}

// Str2Bytes 字符串转Bytes
func Str2Bytes(s string) []byte {
	x := (*[2]uintptr)(unsafe.Pointer(&s))
	h := [3]uintptr{x[0], x[1], x[1]}
	return *(*[]byte)(unsafe.Pointer(&h))
}

// UniqueInts 整型列表去重
func UniqueInts(a []int) (b []int) {
	m := map[int]bool{}
	for _, v := range a {
		if _, ok := m[v]; !ok {
			b = append(b, v)
			m[v] = true
		}
	}
	return
}

//MapToSlice map转数组
func MapToSlice(m map[string]string) []string {
	s := make([]string, 0, len(m))
	for _, v := range m {
		s = append(s, v)
	}
	return s
}

// UniqueStrings 字符串列表去重
func UniqueStrings(a []string) (b []string) {
	m := make(map[string]bool, 0)
	for _, v := range a {
		if _, ok := m[v]; !ok {
			b = append(b, v)
		}
		m[v] = true
	}
	return
}

// UniqueStringsWithoutEmpty 字符串列表去重
func UniqueStringsWithoutEmpty(a []string) (b []string) {
	m := make(map[string]bool, 0)
	for _, v := range a {
		if v == "" {
			continue
		}
		if _, ok := m[v]; !ok {
			b = append(b, v)
		}
		m[v] = true
	}
	return
}

// TrimHTML 过滤html
func TrimHTML(src string) string {
	//将HTML标签全转换成小写
	re, _ := regexp.Compile("\\<[\\S\\s]+?\\>")
	src = re.ReplaceAllStringFunc(src, strings.ToLower)
	//去除STYLE
	re, _ = regexp.Compile("\\<style[\\S\\s]+?\\</style\\>")
	src = re.ReplaceAllString(src, "")
	//去除SCRIPT
	re, _ = regexp.Compile("\\<script[\\S\\s]+?\\</script\\>")
	src = re.ReplaceAllString(src, "")
	//去除所有尖括号内的HTML代码，并换成换行符
	re, _ = regexp.Compile("\\<[\\S\\s]+?\\>")
	src = re.ReplaceAllString(src, "\n")
	//去除连续的换行符
	re, _ = regexp.Compile("\\s{2,}")
	src = re.ReplaceAllString(src, "\n")
	return strings.TrimSpace(src)
}

// HasIntersectionForInt 算两个数组是否存在交集
func HasIntersectionForInt(a []int, b []int) bool {
	bit := make(map[int]bool, len(a))
	for _, v := range a {
		bit[v] = true
	}
	for _, v := range b {
		if _, ok := bit[v]; ok {
			return true
		}
	}
	return false
}

// Int64ToString int64转字符串
func Int64ToString(i int64) string {
	return strconv.FormatInt(i, 10)
}

// Uint64ToString int64转字符串
func Uint64ToString(i uint64) string {
	return strconv.FormatUint(i, 10)
}

// StringToInt64 字符转int64
func StringToInt64(s string) int64 {
	val, _ := strconv.ParseInt(s, 10, 64)
	return val
}

// Shuffle ...
func Shuffle(slice []int) {
	r := rand.New(rand.NewSource(time.Now().Unix()))
	for len(slice) > 0 {
		n := len(slice)
		randIndex := r.Intn(n)
		slice[n-1], slice[randIndex] = slice[randIndex], slice[n-1]
		slice = slice[:n-1]
	}
}

// ShuffleStringArray ...
func ShuffleStringArray(slice []string) {
	r := rand.New(rand.NewSource(time.Now().Unix()))
	for len(slice) > 0 {
		n := len(slice)
		randIndex := r.Intn(n)
		slice[n-1], slice[randIndex] = slice[randIndex], slice[n-1]
		slice = slice[:n-1]
	}
}

// ContainLetter 包含字母
func ContainLetter(s string) bool {
	if s == "" {
		return false

	}
	pattern := `[a-zA-Z]+`
	reg := regexp.MustCompile(pattern)
	return reg.MatchString(s)
}

// ContainNumber 包含数字
func ContainNumber(s string) bool {
	if s == "" {
		return false

	}
	pattern := `[0-9]+`
	reg := regexp.MustCompile(pattern)
	return reg.MatchString(s)
}

// CheckLetterNumber 字符串格式校验
func CheckLetterNumber(s string, min, max int) bool {
	if s == "" {
		return false
	}
	pattern := fmt.Sprintf(`^[a-zA-Z0-9]{%d,%d}$`, min, max)
	reg := regexp.MustCompile(pattern)
	return reg.MatchString(s)
}

// ContainChineseChar 判断是否包含中文字符
func ContainChineseChar(s string) bool {
	for _, r := range s {
		if unicode.Is(unicode.Scripts["Han"], r) || (regexp.MustCompile("[\u3002\uff1b\uff0c\uff1a\u201c\u201d\uff08\uff09\u3001\uff1f\u300a\u300b]").MatchString(string(r))) {
			return true
		}
	}
	return false

}

// IntToString int转字符串
func IntToString(i int) string {
	return strconv.Itoa(i)
}

// StringToInt 字符转int
func StringToInt(s string) int {
	val, _ := strconv.Atoi(s)
	return val
}

// StringLength 字符串长度
func StringLength(s string) int {
	return utf8.RuneCountInString(s)
}

// Substr ...
func Substr(str string, start, length int) string {
	rs := []rune(str)
	rl := len(rs)
	end := 0

	if start < 0 {
		start = rl - 1 + start
	}
	end = start + length

	if start > end {
		start, end = end, start
	}

	if start < 0 {
		start = 0
	}
	if start > rl {
		start = rl
	}
	if end < 0 {
		end = 0
	}
	if end > rl {
		end = rl
	}

	return string(rs[start:end])
}

// IntArrayToInt64Array ...
func IntArrayToInt64Array(array []int) (result []int64) {
	if len(array) <= 0 {
		return
	}

	for _, v := range array {
		result = append(result, int64(v))
	}
	return
}

// ArrayVarAdd 将数据中每个位置的值相加
func ArrayVarAdd(array1 []int, array2 []int) []int {
	array := make([]int, len(array1))
	for i := 0; i < len(array1); i++ {
		array[i] = array1[i] + array2[i]
	}
	return array
}

// ArrayIntCleanEmpty 数组中剔除0值
func ArrayIntCleanEmpty(array []int) []int {
	if len(array) == 0 {
		return array
	}
	dst := make([]int, 0)
	for _, v := range array {
		if v == 0 {
			continue
		}
		dst = append(dst, v)
	}
	return dst
}

// RangeRand 生成区间[-m, n]的安全随机数
func RangeRand(min, max int64) int64 {
	if min > max {
		panic("the min is greater than max!")
	}

	if min < 0 {
		f64Min := math.Abs(float64(min))
		i64Min := int64(f64Min)
		result, _ := crypto_rand.Int(crypto_rand.Reader, big.NewInt(max+1+i64Min))

		return result.Int64() - i64Min
	}

	result, _ := crypto_rand.Int(crypto_rand.Reader, big.NewInt(max-min+1))
	return min + result.Int64()
}

// Base64Encode base64加密
func Base64Encode(data []byte) string {
	return base64.StdEncoding.EncodeToString(data)
}

// Base64Decode base65解密
func Base64Decode(data string) []byte {
	decodeString, _ := base64.StdEncoding.DecodeString(data)
	return decodeString
}

// PriceToString 价格字符转类型
func PriceToString(price float64) string {
	return fmt.Sprintf("%.02f", price)
}

// Convent10To26 十进制转换成26进制
func Convent10To26(n int) string {
	var (
		Str  string = ""
		k    int
		temp []int //保存转化后每一位数据的值，然后通过索引的方式匹配A-Z
	)
	//用来匹配的字符A-Z
	Slice := []string{"", "A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", "N", "O", "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z"}

	if n > 26 { //数据大于26需要进行拆分
		for {
			k = n % 26 //从个位开始拆分，如果求余为0，说明末尾为26，也就是Z，如果是转化为26进制数，则末尾是可以为0的，这里必须为A-Z中的一个
			if k == 0 {
				temp = append(temp, 26)
				k = 26
			} else {
				temp = append(temp, k)
			}
			n = (n - k) / 26 //减去Num最后一位数的值，因为已经记录在temp中
			if n <= 26 {     //小于等于26直接进行匹配，不需要进行数据拆分
				temp = append(temp, n)
				break
			}
		}
	} else {
		return Slice[n]
	}

	for _, value := range temp {
		Str = Slice[value] + Str //因为数据切分后存储顺序是反的，所以Str要放在后面
	}
	return Str
}

// B2S byte转string
func B2S(b []byte) string {
	return *(*string)(unsafe.Pointer(&b))
}

// S2B string转byte
func S2B(s string) (b []byte) {
	bh := (*reflect.SliceHeader)(unsafe.Pointer(&b))
	sh := (*reflect.StringHeader)(unsafe.Pointer(&s))
	bh.Data = sh.Data
	bh.Cap = sh.Len
	bh.Len = sh.Len
	return b
}

// IsNum 判断字符串是否是纯数字
func IsNum(s string) bool {
	_, err := strconv.ParseFloat(s, 64)
	return err == nil
}

//ReplaceArea  省|市|自治区|特别行政区|壮族|回族|维吾尔
func ReplaceArea(s string) string {
	s = strings.Replace(s, "省", "", 1)
	s = strings.Replace(s, "市", "", 1)
	s = strings.Replace(s, "自治区", "", 1)
	s = strings.Replace(s, "特别行政区", "", 1)
	s = strings.Replace(s, "壮族", "", 1)
	s = strings.Replace(s, "回族", "", 1)
	s = strings.Replace(s, "维吾尔", "", 1)
	return s
}

// CalcPassword 明文密码 + slat 生成密文密码
func CalcPassword(password, slat string) string {
	return Sha1(password + slat)
}

// Sha1 sha1
func Sha1(data string) string {
	o := sha1.New()
	o.Write([]byte(data))
	return hex.EncodeToString(o.Sum([]byte("")))
}

// Sha1Byte bytes计算哈希
func Sha1Byte(data []byte) []byte {
	h := sha1.New()
	h.Write(data)
	return h.Sum(nil)
}

func GetCurrentTime() int64 {
	return time.Now().Unix()
}

// CheckIsEmail 验证是否邮箱
func CheckIsEmail(e string) bool {
	emailRegex := regexp.MustCompile("^[a-zA-Z0-9.!#$%&'*+/=?^_`{|}~-]+@[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*$")
	return emailRegex.MatchString(e)
}

// MakeFileMd5 计算文件MD5
func MakeFileMd5(file multipart.File) (md5Str string, err error) {
	if _, err = file.Seek(0, 0); err != nil { // 移动指针，从头开始
		return
	}

	h := md5.New()
	if _, err = io.Copy(h, file); err != nil { // 复制文件
		return
	}

	md5Str = hex.EncodeToString(h.Sum(nil)) // 通过文件生成md5

	if _, err = file.Seek(0, 0); err != nil { // 移动指针，从头开始
		return
	}
	return
}

// PriceToThousands 个价格转千单位，保留整数部分
func PriceToThousands(price int) int {
	return price / 1000
}

// ThousandsToPrice 千价格转个单位
func ThousandsToPrice(price int) int {
	return price * 1000
}

// IfStr  cond ? a : b
func IfStr(cond bool, a, b string) string {
	if cond {
		return a
	}
	return b
}

func TrustJson(obj interface{}) []byte {
	tempByte, _ := json.Marshal(obj)
	tempByte = IfInterface(tempByte == nil, []byte{}, tempByte).([]byte)
	return tempByte
}

// IfInterface cond ? a : b
func IfInterface(cond bool, a, b interface{}) interface{} {
	if cond {
		return a
	}
	return b
}

func IfInterfaceFunc(cond bool, a func() interface{}, b func() interface{}) interface{} {
	if cond {
		return a()
	}
	return b()
}

func RecoverPanic(fixer ...func(err interface{})) {
	if err := recover(); err != nil {
		stacks := GetStacks()
		fmt.Printf("err: %s, stacks: %s", err, stacks)
		if len(fixer) > 0 {
			fixer[0](err)
		}
	}
}

func GetStacks() string {
	var stack []string
	for i := 1; ; i++ {
		_, file, line, ok := runtime.Caller(i)
		if !ok {
			break
		}
		stack = append(stack, fmt.Sprintf("%s:%d", file, line))
	}
	return strings.Join(stack, "\r\n")
}

//SBCConvertDBC 全角转半角（全符号）  ，->, 。->.
func SBCConvertDBC() (bcCase unicode.SpecialCase) {
	bcCase = unicode.SpecialCase{
		unicode.CaseRange{
			0x3002, // Lo 全角句号
			0x3002, // Hi 全角句号
			[unicode.MaxCase]rune{
				0,               // UpperCase
				0x002e - 0x3002, // LowerCase 转成半角句号
				0,               // TitleCase
			},
		},
		//
		unicode.CaseRange{
			0xff01, // Lo: 全角！
			0xFF19, // Hi:到全角 9
			[unicode.MaxCase]rune{
				0,               // UpperCase
				0x0021 - 0xff01, // LowerCase 转成半角
				0,               // TitleCase
			},
		},
	}
	return
}

func ToString(obj interface{}, precisionNum ...int) string {
	if InterfaceIsNil(obj) {
		return ""
	}
	switch reflect.TypeOf(obj).Kind() {
	default:
		return fmt.Sprintf("%v", obj)
	case reflect.Float32, reflect.Float64:
		if len(precisionNum) > 0 {
			format := fmt.Sprintf("%%.%df", precisionNum[0])
			return fmt.Sprintf(format, obj)
		}
		return fmt.Sprintf("%f", obj)
	case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64,
		reflect.Int8, reflect.Int, reflect.Int16, reflect.Int32, reflect.Int64:
		return fmt.Sprintf("%d", obj)
	}
}

func TagMappingStFieldName(tag string, obj interface{}) (map[string]string, error) {
	objT := reflect.ValueOf(obj).Type()
	result := map[string]string{}
	if objT.Kind() == reflect.Ptr {
		if objT.Elem().Kind() != reflect.Struct {
			return result, fmt.Errorf("obj should be struct or pointer to struct")
		}
		objT = objT.Elem()
	}
	if objT.Kind() != reflect.Struct {
		return result, fmt.Errorf("obj should be struct or pointer to struct")
	}
	for i := 0; i < objT.NumField(); i++ {
		tagValue, isExist := objT.Field(i).Tag.Lookup(tag)
		if isExist {
			result[tagValue] = objT.Field(i).Name
		}
	}
	return result, nil
}
